package org.rasterfun

import geneator.CompiledIntGenerator
import language2.Context
import language2.num.{NumVar, Num}
import utils.Rectangle
import org.codehaus.janino.{Scanner, ClassBodyEvaluator}
import java.io.StringReader

/**
 * Calls the given script with each x and y coordinate for a picture, and uses the returned red, green, and blue values
 * (in range 0..1) to calculate the color for the pixel.
 * 
 * @author Hans Haggstrom
 */
case class ArrayGenerator(script: (Num, Num) => (Num, Num, Num)) {

  private var compiled: CompiledIntGenerator = null

  private def compileProgram(source : String): CompiledIntGenerator = {
    try{
      val evaluator = ClassBodyEvaluator.createFastClassBodyEvaluator(
        new Scanner(null, new StringReader(source)),
        classOf[CompiledIntGenerator],
        null)

      evaluator.asInstanceOf[CompiledIntGenerator]
    } catch {
      case e : Throwable => e.printStackTrace ; null  // TODO: Better logging?
    }
  }

  def generateSource(script: (Num, Num) => (Num, Num, Num)): String = {
    
    val source: StringBuilder = new StringBuilder()

    // TODO: Get imports from context, where they 
    source append
    """
      import java.lang.Math;
      import org.rasterfun.utils.SimplexNoise;

      public void generate(int[] target, int width, int height, float x1, float y1, float x2, float y2) {
        float xStep = (x2 - x1) / width;
        float yStep = (y2 - y1) / height;
        int index = 0;
        int cellY = 0;
        for (float y = y1; cellY < height; y += yStep ) {
          int cellX = 0;
          for (float x = x1; cellX < width; x += xStep ) {
    """

    val (r, g, b) = script(NumVar('x), NumVar('y))

    val context: Context = new Context()
    val rCode = r.generateCode(context)
    val gCode = g.generateCode(context)
    val bCode = b.generateCode(context)

    context.generateCode(source)
    source append ("\n")
    source append ("          float r = " + rCode + ";\n")
    source append ("          float g = " + gCode + ";\n")
    source append ("          float b = " + bCode + ";\n")

    source append
    """
            if (r < 0f) r = 0f;
            if (r > 1f) r = 1f;
            if (g < 0f) g = 0f;
            if (g > 1f) g = 1f;
            if (b < 0f) b = 0f;
            if (b > 1f) b = 1f;

            target[index++] =
              (255 << 24) |
              (( ((int)(255 * r)) & 0xFF) << 16) |
              (( ((int)(255 * g)) & 0xFF) << 8) |
              (( ((int)(255 * b)) & 0xFF) << 0);

            cellX++;
          }
          cellY++;
        }
      }
    """

    println(source.toString)

    source.toString
  }

  /**
   * Generates the image to the specified target, using the specified corner coordinates.
   */
  def generate(target: IntRaster, coordinates: Rectangle) {
    if (compiled == null) {
      compiled = compileProgram(generateSource(script))
    }

    compiled.generate(target.rasterData, target.sizeX, target.sizeY, coordinates.x, coordinates.y, coordinates.x2, coordinates.y2)
  }

}

